Skip to content

feat(http): guarded() consumer-side middleware guard wrapper (fs-http 0.5.0)#144

Merged
Goosterhof merged 1 commit into
mainfrom
engineer/fs-http-guarded-middleware-wrapper
Jul 2, 2026
Merged

feat(http): guarded() consumer-side middleware guard wrapper (fs-http 0.5.0)#144
Goosterhof merged 1 commit into
mainfrom
engineer/fs-http-guarded-middleware-wrapper

Conversation

@Goosterhof

Copy link
Copy Markdown
Contributor

What

Adds guarded() — a consumer-side, opt-in middleware guard wrapper — to @script-development/fs-http (0.4.1 → 0.5.0, additive/non-breaking). One generic wraps any of the three (arg) => void middleware bodies so a side-effect throw cannot corrupt the interceptor chain:

service.registerResponseMiddleware(guarded((response) => { /* ...may throw... */ }));

Why

fs-http invokes request/response/response-error middleware as synchronous, un-caught, un-awaited loops inside its axios interceptors. A throwing middleware body rejects a resolved 200 on the success path and masks the real API error on the error path. This is by design — the library stays sync-only + loud (the 2026-05-13 rejection of library-side try/catch holds). A 2026-07-02 fleet recon found every fs-http consumer (entreezuil ×6, emmie ×7, ublgenie ×2, BIO ×3) carries un-guarded middleware of exactly this shape; kendo already hand-guarded its own (WR-0078 / script-development/kendo#1538, n=2).

Rather than hand-roll try/catch across ~18 bodies fleet-wide, guarded() is the canonical primitive — the enforcement-ladder answer. It's opt-in at the registration site: loud library, defensive consumer. Default handler surfaces the swallowed throw via console.error (visible to error trackers); pass a GuardedMiddlewareErrorHandler to route it elsewhere. No re-throw.

Proven closed (end-to-end, against the real interceptor loop)

  • Un-guarded throwing response body → rejects the resolved 200 (contrast test).
  • Guarded throwing response body → 200 still resolves, throw routed to onError.
  • Guarded throwing error body → still rejects with the ORIGINAL AxiosError, not the middleware's throw.

guarded.ts 100% coverage + 100% mutation (6/6). createHttpService and the interceptor loops are unchanged. Pre-1.0 peer-range cascade handled (loading 0.1.5 / adapter-store 0.3.1 / cached-adapter-store 0.2.4); lockfile synced, 0 nested registry copies.

Pairs with the fs-packages CLAUDE.md § Middleware Sync Contract doctrine block (#143) — that block will be cross-linked to guarded() once both land. Precedent: war-room deferred.md [adr] fs-packages-fs-http-async-aware-middleware-rejection-doctrine.

🤖 Generated with Claude Code

@Goosterhof Goosterhof requested a review from a team as a code owner July 2, 2026 13:06
@Goosterhof Goosterhof added the Agent Review Requested Requesting review of specialized AI review agents. label Jul 2, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 2, 2026

Copy link
Copy Markdown

Deploying fs-packages with  Cloudflare Pages  Cloudflare Pages

Latest commit: 89bcee0
Status: ✅  Deploy successful!
Preview URL: https://92ed346a.fs-packages.pages.dev
Branch Preview URL: https://engineer-fs-http-guarded-mid.fs-packages.pages.dev

View logs

@jasperboerhof

Copy link
Copy Markdown
Contributor

Town Crier Review · 9/10 · PASS · 🔎 Independent

fs-packages #144 · AC anchor: PR description (no kendo issue) · head 86170e96a0 · via the town-crier bus (request #208)

Tip

Reviewed the new guarded() HOF (packages/http/src/guarded.ts) plus its type-assignability and end-to-end tests, the docs, and the full fs-http 0.5.0 peer-range cascade: the deliberate throw-swallow survives silent-failure scrutiny (opt-in per-site, loud console.error default, original response/error preserved and proven against the real interceptor loop with a CONTRAST test), the zero-cast claim holds against the (arg)=>void middleware types, and the cascade is complete across all four consumers with clean lock hygiene (one workspace-link entry, no nested copies). No defects found; CI is pending (check, Cloudflare Pages), not red.

No findings — clean against the review checklist.

@jasperboerhof jasperboerhof left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-approved — Town Crier verdict PASS @Head, CI green, no open MAJOR+ thread. Our approval is our independent vote (approve-alongside): a peer's review / CHANGES_REQUESTED never withholds it — we verify every blocker ourselves, and a real one drops our own verdict below PASS. See the verdict comment + inline notes.

@Goosterhof Goosterhof force-pushed the engineer/fs-http-guarded-middleware-wrapper branch from 86170e9 to 89bcee0 Compare July 2, 2026 13:14
@Goosterhof Goosterhof enabled auto-merge July 2, 2026 13:15
@Goosterhof Goosterhof requested a review from jasperboerhof July 2, 2026 13:17
@Goosterhof Goosterhof merged commit da04415 into main Jul 2, 2026
2 checks passed
@Goosterhof Goosterhof deleted the engineer/fs-http-guarded-middleware-wrapper branch July 2, 2026 13:18
@jasperboerhof

Copy link
Copy Markdown
Contributor

Town Crier Review · 9/10 · PASS · 🤝 Confirm

fs-packages #144 · AC anchor: PR description (no kendo issue linked) · head 89bcee0cd0 · via the town-crier bus (request #208)

Tip

Reviewed the additive guarded() consumer-side middleware wrapper and the fs-http 0.5.0 peer-range cascade at head 89bcee0; this corroborates our own prior PASS on this PR — B confirmed all four claims (CONTRAST-tested throw-swallow, zero-cast type assignability, complete 4-package peer cascade with clean lock hygiene, CI pending not red) still hold, and the two reviewed heads differ only by an unrelated rebase (content byte-identical). No defects found.

No findings — clean against the review checklist.

Bus thread · 1 prior review(s):

  • dispatch (independent): Independent first look (empty thread). PASS 9/10 — no findings. Reviewed the new guarded() HOF (packages/http/src/guarded.ts) plus its type-assignability and end-to-end tests, the docs, and the …

@jasperboerhof jasperboerhof left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-approved — Town Crier verdict PASS @Head, CI green, no open MAJOR+ thread. Our approval is our independent vote (approve-alongside): a peer's review / CHANGES_REQUESTED never withholds it — we verify every blocker ourselves, and a real one drops our own verdict below PASS. See the verdict comment + inline notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Agent Review Requested Requesting review of specialized AI review agents.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants